
#include <BBuffer>
#include <BTcpSocket>
#include "member_BSocket.h"
#include "member_BTcpSocket.h"

using namespace BWE;

#define member					(*(member_BTcpSocket*)_ptr)
#define member_allocate()		_ptr = new member_BTcpSocket(this)
#define member_release()		delete (member_BTcpSocket*)_ptr

#ifdef linux
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

BTcpSocket::BTcpSocket()
{
	member_allocate();
	socket_member(this)->create(AF_INET, SOCK_STREAM, 0);
}
BTcpSocket::~BTcpSocket()
{
	member_release();
}

BTcpSocket::Type BTcpSocket::type() const
{
	return TcpSocket;
}

bool BTcpSocket::bind(const BString& ip, int port)
{
	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(ip.cstr());
	
	int ret = ::bind(socket_member(this)->sock, (sockaddr*)&addr, sizeof(addr));
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("bind");
		return false;
	}
	return true;
}

bool BTcpSocket::connectServer(const BString& address)
{
	BStringList words;
	address.split(words, ':');
	if (words.size() == 2)
	{
		int port = words[1].toInt();
		return connectServer(words[0], port);
	}
	return false;
}
bool BTcpSocket::connectServer(const BString& ip, int port)
{
	if (socket_member(this)->sock == 0)
	{
		if (!socket_member(this)->create(AF_INET, SOCK_STREAM, IPPROTO_TCP))
			return false;
	}

	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = inet_addr(ip.cstr());

	int sock = socket_member(this)->sock;
	int ret = ::connect(socket_member(this)->sock, (sockaddr*)&addr, sizeof(addr));
	if(ret == -1)
	{
		socket_member(this)->checkError();
		return false;
	}
	socket_member(this)->state = State_Connecting;
	this->emit(Signal_StateChanged, State_Connecting);
	return true;
}

int BTcpSocket::recv(char* data, int length)
{
	int ret = ::recv(socket_member(this)->sock, data, length, 0);
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("recv");
	}
	return ret;
}
int BTcpSocket::send(const char* data, int length)
{
	int ret = ::send(socket_member(this)->sock, data, length, 0);
	if(ret == -1)
	{
		char* errStr = strerror(errno);
		perror("send");
	}
	return ret;
}

int BTcpSocket::send(const BByteArray& bytes)
{
	return send((char*)bytes.data(), bytes.size());
}
int BTcpSocket::recv(BByteArray& bytes)
{
	int size = 0;
	while (1)
	{
		BByte data[1024];
		int ret = ::recv(socket_member(this)->sock, (char*)data, 1024, 0);
		if (ret == -1)
		{
			socket_member(this)->checkError();
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret == 0)
		{
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret > 0)
		{
			bytes.append(data, ret);
			size += ret;
		}
		if (ret < 1024)
		{
			break;
		}
	}
	return size;
}

int BTcpSocket::send(const BBuffer& buffer)
{
	return send((char*)buffer.bytes().data(), buffer.size());
}
int BTcpSocket::recv(BBuffer& buffer)
{
	int size = 0;
	while (1)
	{
		BByte data[1024];
		int ret = ::recv(socket_member(this)->sock, (char*)data, 1024, 0);
		if (ret == -1)
		{
			socket_member(this)->checkError();
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret == 0)
		{
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret > 0)
		{
			buffer.write(data, ret);
			size += ret;
		}
		if (ret < 1024)
		{
			break;
		}
	}
	return size;
}

int BTcpSocket::send(const BString& text)
{
	return send((char*)text.cstr(), text.size());
}
int BTcpSocket::recv(BString& text)
{
	int size = 0;
	while (1)
	{
		char data[1024];
		int ret = ::recv(socket_member(this)->sock, (char*)data, 1024, 0);
		if (ret == -1)
		{
			socket_member(this)->checkError();
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret == 0)
		{
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret > 0)
		{
			text.append(data, 0, ret);
			size += ret;
		}
		if (ret < 1024)
		{
			break;
		}
	}
	return size;
}

#endif

#ifdef _WIN32

BTcpSocket::BTcpSocket()
{
	member_allocate();
	socket_member(this)->create(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}
BTcpSocket::~BTcpSocket()
{
	member_release();
}

BTcpSocket::Type BTcpSocket::type() const
{
	return TcpSocket;
}

bool BTcpSocket::bind(const BString& ip, int port)
{
	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	inet_pton(AF_INET, ip.cstr(), &(addr.sin_addr.S_un.S_addr));

	int ret = ::bind(socket_member(this)->sock, (sockaddr*)&addr, sizeof(addr));
	if(ret == SOCKET_ERROR)
	{
		int errorCode = WSAGetLastError();
		char* errStr = strerror(errno);
		perror("bind");
		return false;
	}
	return true;
}

bool BTcpSocket::connectServer(const BString& address)
{
	BStringList words;
	address.split(words, ':');
	if (words.size() == 2)
	{
		int port = words[1].toInt();
		return connectServer(words[0], port);
	}
	return false;
}
bool BTcpSocket::connectServer(const BString& ip, int port)
{
	if (socket_member(this)->sock == 0)
	{
		if (!socket_member(this)->create(AF_INET, SOCK_STREAM, IPPROTO_TCP))
			return false;
	}

	sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	InetPton(AF_INET, ip.cstr(), &addr.sin_addr.s_addr);

	SOCKET sock = socket_member(this)->sock;
	int ret = ::connect(socket_member(this)->sock, (sockaddr*)&addr, sizeof(addr));
	if(ret == SOCKET_ERROR)
	{
		socket_member(this)->checkError();
		return false;
	}
	socket_member(this)->state = State_Connecting;
	this->emit(Signal_StateChanged, State_Connecting);
	return true;
}

int BTcpSocket::send(const char* data, int length)
{
	if (!data || length < 1)
		return 0;

	if (socket_member(this)->sock == 0)
		return 0;

	int ret = ::send(socket_member(this)->sock, data, length, 0);
	if (ret == SOCKET_ERROR)
	{
		socket_member(this)->checkError();
		socket_member(this)->state = BSocket::State_Closed;
		emit(Signal_Disconnected);
		this->emit(Signal_StateChanged, State_Closed);
		return ret;
	}
	if (ret == 0)
	{
		socket_member(this)->state = State_Closed;
		emit(Signal_Disconnected);
		this->emit(Signal_StateChanged, State_Closed);
		return ret;
	}
	emit(Signal_BytesSent, ret);
	return ret;
}
int BTcpSocket::recv(char* data, int length)
{
	if (!data || length < 1)
		return 0;

	int ret = ::recv(socket_member(this)->sock, data, length, 0);
	if(ret == SOCKET_ERROR)
	{
		socket_member(this)->checkError();
		this->close();
		emit(Signal_Disconnected);
		return 0;
	}
	if (ret == 0)
	{
		this->close();
	}
	return ret;
}

int BTcpSocket::send(const BByteArray& bytes)
{
	return send((char*)bytes.data(), bytes.size());
}
int BTcpSocket::recv(BByteArray& bytes)
{
	int size = 0;
	while (1)
	{
		BByte data[1024];
		int ret = ::recv(socket_member(this)->sock, (char*)data, 1024, 0);
		if (ret == SOCKET_ERROR)
		{
			socket_member(this)->checkError();
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret == 0)
		{
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret > 0)
		{
			bytes.append(data, ret);
			size += ret;
		}
		if (ret < 1024)
		{
			break;
		}
	}
	return size;
}

int BTcpSocket::send(const BBuffer& buffer)
{
	return send((char*)buffer.bytes().data(), buffer.size());
}
int BTcpSocket::recv(BBuffer& buffer)
{
	int size = 0;
	while (1)
	{
		BByte data[1024];
		int ret = ::recv(socket_member(this)->sock, (char*)data, 1024, 0);
		if (ret == SOCKET_ERROR)
		{
			socket_member(this)->checkError();
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret == 0)
		{
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret > 0)
		{
			buffer.write(data, ret);
			size += ret;
		}
		if (ret < 1024)
		{
			break;
		}
	}
	return size;
}

int BTcpSocket::send(const BString& text)
{
	return send((char*)text.cstr(), text.size());
}
int BTcpSocket::recv(BString& text)
{
	int size = 0;
	while (1)
	{
		char data[1024];
		int ret = ::recv(socket_member(this)->sock, (char*)data, 1024, 0);
		if (ret == SOCKET_ERROR)
		{
			socket_member(this)->checkError();
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret == 0)
		{
			this->close();
			emit(Signal_Disconnected);
			break;
		}
		if (ret > 0)
		{
			text.append(data, 0, ret);
			size += ret;
		}
		if (ret < 1024)
		{
			break;
		}
	}
	return size;
}

#endif
